﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace MoneyBox
{
    public partial class MoneyBox : TextBox
    {
        //Variables de configuración
        private char _separadorMiles = '.';
        private char _simboloMoneda = '₡';

        public MoneyBox()
        {
            InitializeComponent();
        }

        /// <summary>
        /// Propiedad encargada de manejar el Simbolo de la moneda que se desee utilizar, por defecto se utiliza el símbolo del Colón Costarricense(₡)
        /// </summary>
        public char Simbolo_Moneda
        {
            get
            {
                return _simboloMoneda;
            }
            set
            {
                _simboloMoneda = value;
            }
        }

        /// <summary>
        /// Propiedad encargada de manejar el separador de miles que se desee utilizar, por defecto se utiliza el punto(.)
        /// </summary>
        public char Separador_Miles
        {
            get
            {
                return _separadorMiles;
            }
            set
            {
                _separadorMiles = value;
            }
        }
        /// <summary>
        /// Propiedad utilizada para obtener y setear un valor en el campo de moneda
        /// </summary>
        public double Value
        {
            get
            {
                return this.ObtenerValorNumerico(this.Text);
            }
            set
            {
                this.Text = this.ObtenerNumeroFormatoMoneda(value);
            }
        }

        private void MoneyBox_Leave(object sender, EventArgs e)
        {
            //Almacena el valor actual del campo de texto
            string valorActual = this.Text;
            //Esta variable almacenará el valor final del campo de texto
            string valorFinal = string.Empty;

            //Evalua que el campo sea distinto de nulo
            if (valorActual != String.Empty)
            {
                //Si el valor actual es diferente de nulo
                int valorCampo; //Variable encargada de almacenar el valor entero del campos
                bool esNumerico; //Esta bandera se encargará de señalar si el campo actual es numérico
                try
                {
                    //Intenta parsear a entero el valor actual
                    valorCampo = Int32.Parse(valorActual);
                    //Se coloca la badera esNumerico en true
                    esNumerico = true;
                }
                catch (Exception ex)
                {
                    //Hubo un error al parsear el dato, por lo que coloca la bandera esNumerico en false
                    esNumerico = false;
                }
                //Evalúa si no es numerico
                if (esNumerico == false)
                {
                    //Como no es numérico entonces evalúa si el primer caracter es el simbolo de la moneda
                    if (valorActual.Substring(0, 1).ToString() == _simboloMoneda.ToString())
                    {
                        //Si el primer caracter es el tipo de moneda entonces se lo quita y almacena el valor actual
                        valorActual = valorActual.Substring(1);
                    }
                    else
                    {
                        //Si no es numerico y el primer caracter no es el simbolo de moneda entonces lanza una excepcion
                        throw new Exception("Se están introduciendo valores tipo texto. No se puede realizar la operación");
                    }
                }

                int tamanioNumero = valorActual.Length; //Almancena el tamaño del valor actual
                valorCampo = Int32.Parse(valorActual); //Parsea a entero el valor actual
                int residuo = tamanioNumero % 3;      //Obtiene el residuo de la división entre el tamaño del Numero entre 3 (que es la posición de la coma)
                int dividendo = tamanioNumero / 3;    //Obtiene el dividendo de la división entre el tamaño del Numero entre 3 (que es la posición de la coma)                
                char[] arregloValor = valorActual.ToCharArray(); //Convierte el numero en un arreglo de caracteres
                int tamanioVector = tamanioNumero + dividendo; //Este tamaño es el del nuevo numero
                //Evalua si el residuo fue cero, esto quiere decir que el número es divisible entre 3
                if (residuo == 0)
                {
                    //Si fue cero entonces le resta 1 al tamaño del nuevo numero
                    tamanioVector -= 1;
                }
                //obtiene el valor númerico, con el tipo de moneda y los separadores de miles
                valorFinal = this.ObtenerNumeroFormatoMoneda(arregloValor, tamanioVector);

            }
            this.Text = valorFinal;
        }

        private void MoneyBox_KeyPress(object sender, KeyPressEventArgs e)
        {
            //Evalua que la tecla presionada sea un número o el backspace
            if (e.KeyChar >= 48 && e.KeyChar <= 57 || e.KeyChar == 8)
                e.Handled = false; //Deja que el evento fluya con normalidad
            else
                e.Handled = true; //Detiene el evento
        }

        private void MoneyBox_Enter(object sender, EventArgs e)
        {
            //Almacena el valor actual del campo de texto
            string valorActual = this.Text;
            //Esta variable almacenará el valor final del campo de texto
            string valorFinal = string.Empty;

            //Evalua que el campo sea distinto de nulo
            if (valorActual != String.Empty)
            {
                //Si el valor actual es diferente de nulo
                int valorCampo; //Variable encargada de almacenar el valor entero del campos
                bool esNumerico; //Esta bandera se encargará de señalar si el campo actual es numérico
                try
                {
                    //Intenta parsear a entero el valor actual
                    valorCampo = Int32.Parse(valorActual);
                    //Se coloca la badera esNumerico en true
                    esNumerico = true;
                }
                catch (Exception ex)
                {
                    //Hubo un error al parsear el dato, por lo que coloca la bandera esNumerico en false
                    esNumerico = false;
                }
                //Evalúa si no es numerico
                if (esNumerico == false)
                {
                    //Como no es numérico entonces evalúa si el primer caracter es el simbolo de la moneda
                    if (valorActual.Substring(0, 1).ToString() == _simboloMoneda.ToString())
                    {
                        //Si el primer caracter es el tipo de moneda entonces se lo quita y almacena el valor actual
                        valorActual = valorActual.Substring(1);
                    }
                    else
                    {
                        //Si no es numerico y el primer caracter no es el simbolo de moneda entonces lanza una excepcion
                        throw new Exception("Se están introduciendo valores tipo texto. No se puede realizar la operación");
                    }
                }
                //Almacena el numero con comas en un arreglo de caracteres
                char[] arregloActual = valorActual.ToCharArray();
                //Este for es el encargado de recorres el arreglo de caracteres
                for (int contador = 0; contador <= arregloActual.Length - 1; contador++)
                {
                    //Evalua si el caracter no es un separador de miles
                    if (arregloActual[contador].ToString() != _separadorMiles.ToString())
                    {
                        //Si no es una coma lo agrega al valor final
                        valorFinal += arregloActual[contador];
                    }
                }
            }
            //Coloca el valor final en el campo de texto
            this.Text = valorFinal;
        }

        /// <summary>
        /// Método encargado de devolver un string con el número en formato de moneda
        /// </summary>
        /// <param name="arregloInicial">Numero en un arreglo de caracteres</param>
        /// <param name="tamanioVector">Tamaño del nuevo número (cantidad de números que tendrá)</param>
        /// <returns>Numero en formato de Moneda</returns>
        private string ObtenerNumeroFormatoMoneda(char[] arregloInicial, int tamanioVector)
        {
            string retorno = String.Empty; //Variable que almacenará el número en formato de moneda
            string[] arregloFinal = new string[tamanioVector]; //arreglo que almacenará el nuevo número
            int contarTres = 1; //variable utilizada para saber cuando hay que colocar un separador de miles
            int contadorFinal = tamanioVector - 1; //Contador que manejará arreglo Final

            //Acomoda el nuevo vector con las respectivas comas
            for (int contador = arregloInicial.Length - 1; contador >= 0; contador--)
            {
                //Evalua si aún no se debe colocar una coma
                if (contarTres <= 3)
                {
                    //Si contarTres <= 3 quiere decir que aún no hace falta el separador de miles
                    //Agrega en la posición del contadorFinal el valor en el arregloInicial en la posición contador
                    arregloFinal[contadorFinal] = arregloInicial[contador].ToString();
                    //Disminuye en 1 el contador final
                    contadorFinal--;
                    //Aumenta en 1 el contarTres
                    contarTres++;
                }
                else
                {
                    //Quiere decir que se debe colocar un separador de miles
                    arregloFinal[contadorFinal] = _separadorMiles.ToString();
                    arregloFinal[contadorFinal - 1] = arregloInicial[contador].ToString();
                    //Pone en contador en 2, para que vuelva a contar otros dos más para colocar otra coma
                    contarTres = 2;
                    //Resta dos al contadorFinal
                    contadorFinal -= 2;
                }
            }
            //Asigna, como primer caracter, el simbolo de la moneda
            retorno = _simboloMoneda.ToString();
            //Este For se encarga de concatenar todo el arregloFinal con el string retorno
            for (int contador = 0; contador <= arregloFinal.Length - 1; contador++)
            {
                retorno += arregloFinal[contador];
            }
            //Retorna el numero en formato de moneda
            return retorno;
        }

        /// <summary>
        /// Método encargado de obtener el valor numerico del campo de texto
        /// </summary>
        /// <param name="valorActual">Texto del campo de texto, con formato de moneda</param>
        /// <returns>Valor numérico del campo de texto</returns>
        private double ObtenerValorNumerico(string valorActual)
        {
            string valorFinal = string.Empty;
            //Evalua que el campo sea distinto de nulo
            if (valorActual != String.Empty)
            {
                //Si el valor actual es diferente de nulo
                int valorCampo; //Variable encargada de almacenar el valor entero del campos
                bool esNumerico; //Esta bandera se encargará de señalar si el campo actual es numérico
                try
                {
                    //Intenta parsear a entero el valor actual
                    valorCampo = Int32.Parse(valorActual);
                    //Se coloca la badera esNumerico en true
                    esNumerico = true;
                }
                catch (Exception ex)
                {
                    //Hubo un error al parsear el dato, por lo que coloca la bandera esNumerico en false
                    esNumerico = false;
                }
                //Evalúa si no es numerico
                if (esNumerico == false)
                {
                    //Como no es numérico entonces evalúa si el primer caracter es el simbolo de la moneda
                    if (valorActual.Substring(0, 1).ToString() == _simboloMoneda.ToString())
                    {
                        //Si el primer caracter es el tipo de moneda entonces se lo quita y almacena el valor actual
                        valorActual = valorActual.Substring(1);
                    }
                    else
                    {
                        //Si no es numerico y el primer caracter no es el simbolo de moneda entonces lanza una excepcion
                        throw new Exception("Se están introduciendo valores tipo texto. No se puede realizar la operación");
                    }
                }
                //Almacena el numero con comas en un arreglo de caracteres
                char[] arregloActual = valorActual.ToCharArray();
                //Este for es el encargado de recorres el arreglo de caracteres
                for (int contador = 0; contador <= arregloActual.Length - 1; contador++)
                {
                    //Evalua si el caracter no es un separador de miles
                    if (arregloActual[contador].ToString() != _separadorMiles.ToString())
                    {
                        //Si no es una coma lo agrega al valor final
                        valorFinal += arregloActual[contador];
                    }
                }
            }
            double valorDouble = double.Parse(valorFinal);
            return valorDouble;
        }

        /// <summary>
        /// Método encargado de obtener el número en formato de moneda
        /// </summary>
        /// <param name="valorNumerico">Valor numerico que se quiere tenga formato de moneda</param>
        /// <returns>´Devuelve el número en formato de momeda</returns>
        private string ObtenerNumeroFormatoMoneda(double valorNumerico)
        {
            //Almacena el valor actual con el valor de entrada
            string valorActual = int.Parse(valorNumerico.ToString()).ToString();
            //Esta variable almacenará el valor final del campo de texto
            string valorFinal = string.Empty;

            //Evalua que el campo sea distinto de nulo
            if (valorActual != String.Empty)
            {
                //Si el valor actual es diferente de nulo
                int valorCampo; //Variable encargada de almacenar el valor entero del campos
                bool esNumerico; //Esta bandera se encargará de señalar si el campo actual es numérico
                try
                {
                    //Intenta parsear a entero el valor actual
                    valorCampo = Int32.Parse(valorActual);
                    //Se coloca la badera esNumerico en true
                    esNumerico = true;
                }
                catch (Exception ex)
                {
                    //Hubo un error al parsear el dato, por lo que coloca la bandera esNumerico en false
                    esNumerico = false;
                }
                //Evalúa si no es numerico
                if (esNumerico == false)
                {
                    //Como no es numérico entonces evalúa si el primer caracter es el simbolo de la moneda
                    if (valorActual.Substring(0, 1).ToString() == _simboloMoneda.ToString())
                    {
                        //Si el primer caracter es el tipo de moneda entonces se lo quita y almacena el valor actual
                        valorActual = valorActual.Substring(1);
                    }
                    else
                    {
                        //Si no es numerico y el primer caracter no es el simbolo de moneda entonces lanza una excepcion
                        throw new Exception("Se están introduciendo valores tipo texto. No se puede realizar la operación");
                    }
                }

                int tamanioNumero = valorActual.Length; //Almancena el tamaño del valor actual
                valorCampo = Int32.Parse(valorActual); //Parsea a entero el valor actual
                int residuo = tamanioNumero % 3;      //Obtiene el residuo de la división entre el tamaño del Numero entre 3 (que es la posición de la coma)
                int dividendo = tamanioNumero / 3;    //Obtiene el dividendo de la división entre el tamaño del Numero entre 3 (que es la posición de la coma)                
                char[] arregloValor = valorActual.ToCharArray(); //Convierte el numero en un arreglo de caracteres
                int tamanioVector = tamanioNumero + dividendo; //Este tamaño es el del nuevo numero
                //Evalua si el residuo fue cero, esto quiere decir que el número es divisible entre 3
                if (residuo == 0)
                {
                    //Si fue cero entonces le resta 1 al tamaño del nuevo numero
                    tamanioVector -= 1;
                }
                //obtiene el valor númerico, con el tipo de moneda y los separadores de miles
                valorFinal = this.ObtenerNumeroFormatoMoneda(arregloValor, tamanioVector);
            }
            return valorFinal;
        }
    }
}
